github.com/quinndk/ethereum_read@v0.0.0-20181211143958-29c55eec3237/Docs/0x07 Block.md (about)

     1  # 以太坊源码研读0x07 Block
     2  
     3  前面看了以太坊的交易模块,而交易都是要打包在区块上的。Block是Eth上存储价值信息的核心数据结构之一。
     4  
     5  一个完整的Block大概包括以下几部分:
     6  
     7  - 1.所有账户的相关活动,都是以Transaction格式存储,每个Block有一个Tx的列表
     8  
     9  - 2.每个交易的执行结果,由一个Receipt对象与其包含的一组Log对象记录
    10  
    11  - 3.所有交易执行完后生成的Receipt列表,存储在Block中(经过压缩加密)
    12  
    13  - 4.不同Block之间,通过前向指针ParentHash一个一个串联起来成为一个单向链表,BlockChain 结构体管理着这个链表
    14  
    15  - 5.Block结构体基本可分为Header和Body两个部分
    16  
    17  # 废话少说撸代码
    18  
    19  ### Block结构
    20  
    21  ```
    22  // Block represents an entire block in the Ethereum blockchain.
    23  type Block struct {
    24  	// 区块头
    25  	header       *Header
    26  	// 叔块的区块头
    27  	uncles       []*Header
    28  	// 交易列表
    29  	transactions Transactions
    30  
    31  	// caches
    32  	hash atomic.Value
    33  	size atomic.Value
    34  
    35  	// Td is used by package core to store the total difficulty
    36  	// of the chain up to and including the block.
    37  	// totalDifficulty 区块总难度  当前区块难度值 = td - lastBlock.td
    38  	td *big.Int
    39  
    40  	// These fields are used by package eth to track
    41  	// inter-peer block relay.
    42  	// 出块时间
    43  	ReceivedAt   time.Time
    44  	// 区块体
    45  	ReceivedFrom interface{}
    46  }
    47  ```
    48  一个Block的唯一标识符就是它的hash,这里的hash是指其Header内容的RLP哈希值。在第一次计算后会缓存到hash值里。
    49  
    50  ```
    51  // Hash returns the keccak256 hash of b's header.
    52  // The hash is computed on the first call and cached thereafter.
    53  func (b *Block) Hash() common.Hash {
    54  	if hash := b.hash.Load(); hash != nil {
    55  		return hash.(common.Hash)
    56  	}
    57  	v := b.header.Hash()
    58  	b.hash.Store(v)
    59  	return v
    60  }
    61  ...
    62  // Hash returns the block hash of the header, which is simply the keccak256 hash of its
    63  // RLP encoding.
    64  func (h *Header) Hash() common.Hash {
    65  	return rlpHash(h)
    66  }
    67  ..
    68  func rlpHash(x interface{}) (h common.Hash) {
    69  	hw := sha3.NewKeccak256()
    70  	rlp.Encode(hw, x)
    71  	hw.Sum(h[:0])
    72  	return h
    73  }
    74  ```
    75  
    76  这里每一个Block都有一个BlockHeader,Header是Block的核心,它的成员变量全都是公共的,可以很方便的向调用者提供关于Block属性的操作。
    77  
    78  ```
    79  // Header represents a block header in the Ethereum blockchain.
    80  type Header struct {
    81  	// 父区块hash,即链上上一个区块的Hash
    82  	ParentHash  common.Hash    `json:"parentHash"       gencodec:"required"`
    83  	// 叔块集合uncles的RLP哈希值
    84  	UncleHash   common.Hash    `json:"sha3Uncles"       gencodec:"required"`
    85  	// 挖出区块的矿工地址
    86  	Coinbase    common.Address `json:"miner"            gencodec:"required"`
    87  	// MPT状态树根哈希
    88  	Root        common.Hash    `json:"stateRoot"        gencodec:"required"`
    89  	// 交易树根节点RLP哈希值
    90  	TxHash      common.Hash    `json:"transactionsRoot" gencodec:"required"`
    91  	// 收据树根节点RLP哈希值
    92  	ReceiptHash common.Hash    `json:"receiptsRoot"     gencodec:"required"`
    93  	// Bloom过滤器(Filter),用来快速判断一个参数Log对象是否存在于一组已知的Log集合中
    94  	Bloom       Bloom          `json:"logsBloom"        gencodec:"required"`
    95  	// 区块难度
    96  	Difficulty  *big.Int       `json:"difficulty"       gencodec:"required"`
    97  	// 区块序号,相当于Bitcoin的Height
    98  	Number      *big.Int       `json:"number"           gencodec:"required"`
    99  	// 区块内所有Gas消耗的理论上限
   100  	GasLimit    uint64         `json:"gasLimit"         gencodec:"required"`
   101  	// 区块内所有Transaction执行时所实际消耗的Gas总和
   102  	GasUsed     uint64         `json:"gasUsed"          gencodec:"required"`
   103  	// 出块时间
   104  	Time        *big.Int       `json:"timestamp"        gencodec:"required"`
   105  	// 额外数据
   106  	Extra       []byte         `json:"extraData"        gencodec:"required"`
   107  	// 用于POW
   108  	MixDigest   common.Hash    `json:"mixHash"          gencodec:"required"`
   109  	// 用于POW 结合MixDigest生成区块哈希值
   110  	Nonce       BlockNonce     `json:"nonce"            gencodec:"required"`
   111  }
   112  ```
   113  此外,以太坊将一个Block中的交易集合和叔块集合单独封装到一个Body结构中,因为他们相对于Header需要更多的内存空间,在传输和验证时为了节省时间可以和Header分开进行。
   114  
   115  ```
   116  // Body is a simple (mutable, non-safe) data container for storing and moving
   117  // a block's data contents (transactions and uncles) together.
   118  // Body可以理解为Block里的数组成员集合,它相对于Header需要更多的内存空间,
   119  // 所以在数据传输和验证时,往往与Header是分开进行的。
   120  type Body struct {
   121  	Transactions []*Transaction
   122  	Uncles       []*Header
   123  }
   124  ```
   125  
   126  我们注意到,这里相比Bitcoin多了一个叔块(uncle)的概念。这里可以参考官方对[叔块](https://github.com/ethereum/wiki/wiki/Design-Rationale#uncle-incentivization)的解释。
   127  
   128  叔块,顾名思义就是跟自己的父区块在一个高度上。我们知道相比Bitcoin,Eth将出块时间缩短到了15s左右。这样在庞大的P2P网络中就增大了同时出现同一高度区块的概率,这样就有可能使得大批矿工因为产生这样的区块而得不到奖励。因此,以太坊引入了叔块的概念,一个叔块该满足的条件为:
   129  
   130  - 1.叔块必须是B的第K层祖先,2<= k <= 7
   131  - 2.叔块不能是B的祖先
   132  - 3.叔块必须拥有合法的block Header
   133  - 4.叔块必须是不曾被包含进区块链过的
   134  
   135  这样,以太坊的激励机制就有所改变。当一个矿工挖到一个普通区块B时,若该区块拥有叔块,该矿工将除固定区块奖励外额外再得到__固定区块奖励/32*count(uncles)__的奖励。
   136  同时,曾经挖出叔块的矿工也将获得__(Number(uncle)+8-Number(B))*固定区块奖励/8__的叔块奖励。我们举个简单的小🌰:
   137  
   138  A挖到一个Number为20的Block,该区块由两个叔块uncle1(B挖出Number=18)和uncle2(C挖出Number=13),此时的奖励分配为:
   139  
   140  - A = 3 + 3/32*2 = 3.1875eth
   141  
   142  - B = (18 + 8 - 20) * 3 / 8 = 3*6/8 = 2.25eth
   143  
   144  - C = (13 + 8 - 20) * 3 / 8 = 3*1/8 = 0.375eth
   145  
   146  关于区块奖励的源码今天不是重点,这里只是稍微提一下。
   147  
   148  ### Block基本操作
   149  
   150  首先来看添加新Block的操作,代码逻辑清晰简单。
   151  
   152  ```
   153  // NewBlock creates a new block. The input data is copied,
   154  // changes to header and to the field values will not affect the
   155  // block.
   156  //
   157  // The values of TxHash, UncleHash, ReceiptHash and Bloom in header
   158  // are ignored and set to values derived from the given txs, uncles
   159  // and receipts.
   160  func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block {
   161  	b := &Block{header: CopyHeader(header), td: new(big.Int)}
   162  
   163  	// TODO: panic if len(txs) != len(receipts)
   164  	if len(txs) == 0 {
   165  		b.header.TxHash = EmptyRootHash
   166  	} else {
   167  		b.header.TxHash = DeriveSha(Transactions(txs))
   168  		b.transactions = make(Transactions, len(txs))
   169  		copy(b.transactions, txs)
   170  	}
   171  
   172  	if len(receipts) == 0 {
   173  		b.header.ReceiptHash = EmptyRootHash
   174  	} else {
   175  		b.header.ReceiptHash = DeriveSha(Receipts(receipts))
   176  		b.header.Bloom = CreateBloom(receipts)
   177  	}
   178  
   179  	if len(uncles) == 0 {
   180  		b.header.UncleHash = EmptyUncleHash
   181  	} else {
   182  		b.header.UncleHash = CalcUncleHash(uncles)
   183  		b.uncles = make([]*Header, len(uncles))
   184  		for i := range uncles {
   185  			b.uncles[i] = CopyHeader(uncles[i])
   186  		}
   187  	}
   188  
   189  	return b
   190  }
   191  
   192  // NewBlockWithHeader creates a block with the given header data. The
   193  // header data is copied, changes to header and to the field values
   194  // will not affect the block.
   195  func NewBlockWithHeader(header *Header) *Block {
   196  	return &Block{header: CopyHeader(header)}
   197  }
   198  ```
   199  
   200  至此,关于Block数据结构的源码就看完了。
   201  
   202   
   203  
   204  
   205  
   206